package com.dtranx.tools.corpora.ability.search;

import co.elastic.clients.elasticsearch._types.FieldValue;
import co.elastic.clients.elasticsearch._types.InlineScript;
import co.elastic.clients.elasticsearch._types.Script;
import co.elastic.clients.elasticsearch._types.query_dsl.BoolQuery;
import co.elastic.clients.elasticsearch._types.query_dsl.FunctionBoostMode;
import co.elastic.clients.elasticsearch._types.query_dsl.Query;
import co.elastic.clients.json.JsonData;
import com.dtranx.tools.commons.util.ScanSupport;
import com.dtranx.tools.corpora.ability.constants.EsConstants;
import com.dtranx.tools.corpora.ability.enums.SearchType;
import com.dtranx.tools.corpora.ability.index.AbstractIndex;
import com.dtranx.tools.corpora.ability.vo.MatchingVo;
import com.dtranx.tools.corpora.abilityapi.enums.SearchMode;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;

import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * @ClassName AbstractSearch
 * @Description TODO
 * @Date 2024/3/12 16:36
 * @Author ly
 **/
@Slf4j
public abstract class AbstractModeSearch {

    protected static final Map<SearchMode, AbstractModeSearch> SEARCH_MODE = Maps.newHashMap();


    static{
        Class clazz = AbstractModeSearch.class;
        Set<Class<?>> classes = null;
        try {
            classes = ScanSupport.classInfos(clazz.getPackage().getName());
        } catch (Exception e) {
            log.error("扫描搜索模式策略失败:",e);
        }
        if(CollectionUtils.isNotEmpty(classes)) {
            for (Class<?> aClass : classes) {
                if(ScanSupport.validate(aClass.getSuperclass(),clazz)){
                    try {
                        AbstractModeSearch abstractModeStrategy = (AbstractModeSearch) aClass.newInstance();
                        SearchMode mode = abstractModeStrategy.getMode();
                        SEARCH_MODE.put(mode,abstractModeStrategy);
                    } catch (Exception e) {
                        log.error("初始化搜索模式策略失败:",e);
                    }
                }
            }
        }
    }

    /**
     * 构建query
     * 如果四其它类型的查询，可以覆盖该方法
     * @param matchingVo
     * @return
     */
    public static Query buildQueryBuilder(MatchingVo matchingVo){
        AbstractModeSearch abstractModeSearch = SEARCH_MODE.get(matchingVo.getSearchMode());
        return Query.of(queryBuilder -> {
            queryBuilder.bool(boolQueryBuilder -> {
                abstractModeSearch.buildNecessaryQuery(boolQueryBuilder,matchingVo);
                if(matchingVo.isAnalyze()){
                    abstractModeSearch.buildAnalyzeQueryBuilder0(boolQueryBuilder,matchingVo);
                }else{
                    abstractModeSearch.buildQueryBuilder0(boolQueryBuilder,matchingVo);
                }
                return boolQueryBuilder;
            });
            return queryBuilder;
        });
    }

    /**
     * 构建boolBuilder
     * @param matchingVo
     * @param boolQueryBuilder
     */
    public static void buildBoolBuilder(MatchingVo matchingVo,BoolQuery.Builder boolQueryBuilder){
        SEARCH_MODE.get(matchingVo.getSearchMode()).buildQueryBuilder0(boolQueryBuilder,matchingVo);
    }

    /**
     * 构建必要的条件
     * @param queryBuilder
     * @param matchingVo
     */
    protected void buildNecessaryQuery(BoolQuery.Builder queryBuilder,MatchingVo matchingVo){
        if(StringUtils.isNotEmpty(matchingVo.getLang())){
            queryBuilder.filter(filterBuilder->{
                    filterBuilder.term(termBuilder -> {
                        termBuilder.field(matchingVo.isReverse() ? AbstractIndex.T_LANG : AbstractIndex.O_LANG);
                        termBuilder.value(matchingVo.getLang());
                        return termBuilder;
                    });
                return filterBuilder;
            });
        }
        if(CollectionUtils.isNotEmpty(matchingVo.getDbIds())){
            queryBuilder.filter(filterBuilder->
                filterBuilder.terms(termBuilder -> {
                termBuilder.field(AbstractIndex.DB_ID);
                termBuilder.terms(termField -> {
                    termField.value(matchingVo.getDbIds().stream().map(FieldValue::of).collect(Collectors.toList()));
                    return termField;
                });
                return termBuilder;
            }));
        }
//        if(CollectionUtils.isNotEmpty(matchingVo.getDbIds())){
//            queryBuilder.filter(filterBuilder->
//                filterBuilder.terms(termBuilder -> {
//                termBuilder.field(AbstractIndex.DB_ID);
//                termBuilder.terms(termField -> {
//                    termField.value(matchingVo.getDbIds().stream().map(FieldValue::of).collect(Collectors.toList()));
//                    return termField;
//                });
//                return termBuilder;
//            }));
//        }
//        if(CollectionUtils.isNotEmpty(matchingVo.getDbIds())){
//            queryBuilder.must(Query.of(mustCase -> {
//                mustCase.terms(termBuilder -> {
//                    termBuilder.field(AbstractIndex.DB_ID);
//                    termBuilder.terms(termField -> {
//                        termField.value(matchingVo.getDbIds().stream().map(FieldValue::of).collect(Collectors.toList()));
//                        return termField;
//                    });
//                    return termBuilder;
//                });
//                return mustCase;
//            }));
//        }
//        if(StringUtils.isEmpty(matchingVo.getLang())){
//            return;
//        }
//        queryBuilder.must(Query.of(mustCase -> {
//            mustCase.term(termBuilder -> {
//                termBuilder.field(matchingVo.isReverse() ? AbstractIndex.T_LANG : AbstractIndex.O_LANG);
//                termBuilder.value(matchingVo.getLang());
//                return termBuilder;
//            });
//            return mustCase;
//        }));
    }

    /**
     * 构建查询条件
     * @param matchingVo
     * @param value
     * @return
     */
    protected List<Query> generateQueries(MatchingVo matchingVo, String value){
        List<Query> queries = Lists.newArrayList();
        Map<String, JsonData> paramsMap = getParamsMap(matchingVo,value);
        queries.add(Query.of(queryBuilder -> {
            queryBuilder.functionScore(functionScoreQueryBuilder->{
                functionScoreQueryBuilder.functions(functionScoreBuilder->{
                    functionScoreBuilder.scriptScore(scriptFunctionBuilder->{
                        scriptFunctionBuilder.script(Script.of(scriptBuilder->{
                            scriptBuilder.inline(InlineScript.of(inlineScriptBuilder->{
                                inlineScriptBuilder.source(getScript());
                                inlineScriptBuilder.lang(EsConstants.SCRIPT_LANG);
                                inlineScriptBuilder.params(paramsMap);
                                return inlineScriptBuilder;
                            }));
                            return scriptBuilder;
                        }));
                        return scriptFunctionBuilder;
                    });
                    return functionScoreBuilder;
                });
                functionScoreQueryBuilder.boostMode(FunctionBoostMode.Replace);
                return functionScoreQueryBuilder;
            });
            //包含匹配
            return queryBuilder;
        }));
        return queries;
    }

    /**
     * 获取脚本内容
     * @return
     */
    protected String getScript(){
        return EsConstants.ES_CUSTOM_SCORE_SCRIPT;
    }

    /**
     * 获取参数
     * @return
     */
    protected Map<String, JsonData> getParamsMap(MatchingVo matchingVo, String value){
        Map<String, JsonData> paramsMap = Maps.newHashMap();
        paramsMap.put(EsConstants.EDIT_DISTANCE_ES_SCORE_SCRIPT_PARAM_1,JsonData.of(matchingVo.getCompressValue() == null ? "" : matchingVo.getCompressValue()));
        paramsMap.put(EsConstants.EDIT_DISTANCE_ES_SCORE_SCRIPT_PARAM_2,JsonData.of(value));
        paramsMap.put(EsConstants.EDIT_DISTANCE_ES_SCORE_SCRIPT_PARAM_3,JsonData.of(matchingVo.isReverse()));
        return paramsMap;
    }

    protected List<Query> generateQueries0(MatchingVo matchingVo, String value){
        List<Query> queries = Lists.newArrayList();
        // 分词匹配
        queries.add(matchQuery(matchingVo, value));
        if (BooleanUtils.isTrue(matchingVo.getFlag())) {
            queries.add(buildLenFilter(matchingVo, value));
        }
        return queries;
    }

    /**
     * 构建should查询
     * @param boolBuilder
     * @param matchingVo
     */
    public static void buildShouldQuery(BoolQuery.Builder boolBuilder, MatchingVo matchingVo) {
        boolBuilder.should(shouldBuilder ->
                shouldBuilder.bool(innerBool -> {
                    buildBoolBuilder(matchingVo, innerBool);
                    return innerBool;
                })
        );
    }

    /**
     * 构建term查询
     * @param matchingVo
     * @param value
     * @return
     */
    protected Query termQuery(MatchingVo matchingVo, String value) {
        return Query.of(query ->
                query.term(termBuilder -> {
                    termBuilder.field(matchingVo.isReverse() ? SearchType.KEYWORD_TYPE.getTranslationKey() : SearchType.KEYWORD_TYPE.getOriginalKey());
                    termBuilder.value(value);
                    termBuilder.boost(matchingVo.getBoost());
                    return termBuilder;
                })
        );
    }

    /**
     * 构建match查询
     * @param matchingVo
     * @param value
     * @return
     */
    protected Query matchQuery(MatchingVo matchingVo, String value) {
        return Query.of(query ->
                query.match(matchBuilder -> {
                    SearchType searchType = matchingVo.isMinor() ? SearchType.TEXT_ANALYZER_STANDARD : SearchType.TEXT_ANALYZER_IK;
                    matchBuilder.field(matchingVo.isReverse() ? searchType.getTranslationKey() : searchType.getOriginalKey());
                    matchBuilder.minimumShouldMatch(StringUtils.isEmpty(matchingVo.getFuzzy()) ? EsConstants.DEFAULT_FUZZY : matchingVo.getFuzzy());
                    matchBuilder.query(value);
                    matchBuilder.boost(matchingVo.getBoost());
                    return matchBuilder;
                })
        );
    }

    /**
     * 构建match_phrase查询
     * @param matchingVo
     * @param value
     * @return
     */
    protected Query matchPhraseQuery(MatchingVo matchingVo, String value) {
        return Query.of(query ->
                query.matchPhrase(phraseBuilder -> {
                    SearchType searchType = matchingVo.isMinor() ? SearchType.TEXT_ANALYZER_STANDARD : SearchType.TEXT_ANALYZER_IK;
                    phraseBuilder.field(matchingVo.isReverse() ? searchType.getTranslationKey() : searchType.getOriginalKey());
                    phraseBuilder.query(value);
                    phraseBuilder.boost(matchingVo.getBoost());
                    return phraseBuilder;
                })
        );
    }

    /**
     * 构建match_phrase_prefix查询
     * @param matchingVo
     * @param value
     * @return
     */
    protected Query matchPhrasePrefixQuery(MatchingVo matchingVo, String value) {
        return Query.of(query ->
                query.matchPhrasePrefix(prefixBuilder -> {
                    SearchType searchType = matchingVo.isMinor() ? SearchType.TEXT_ANALYZER_STANDARD : SearchType.TEXT_ANALYZER_IK;
                    prefixBuilder.field(matchingVo.isReverse() ? searchType.getTranslationKey() : searchType.getOriginalKey());
                    prefixBuilder.query(value);
                    prefixBuilder.boost(matchingVo.getBoost());
                    return prefixBuilder;
                })
        );
    }

    /**
     * 构建长度过滤器
     * @param matchingVo 匹配信息
     * @param value 值
     * @return 构建长度过滤器
     */
    protected Query buildLenFilter(MatchingVo matchingVo, String value){
        String fuzzy = StringUtils.isEmpty(matchingVo.getFuzzy()) ? EsConstants.DEFAULT_FUZZY : matchingVo.getFuzzy();
        double matchRate = Integer.parseInt(fuzzy.replaceAll("%",""))/100D;
        int lenLimit = (int) (value.length() * matchRate);
        SearchType searchType = SearchType.TEXT_LEN;
        return Query.of(query->{
            query.range(range->{
                range.field(matchingVo.isReverse() ? searchType.getTranslationKey() : searchType.getOriginalKey());
                range.lte(JsonData.of(value.length() + lenLimit));
                range.gte(JsonData.of(value.length() - lenLimit));
                return range;
            });
            return query;
        });
    }

    /**
     * 构建查询参数
     * @param queryBuilder
     */
    public abstract void buildQueryBuilder0(BoolQuery.Builder queryBuilder, MatchingVo matchingVo);

    /**
     * 构建分词查询参数
     * @param queryBuilder
     */
    public void buildAnalyzeQueryBuilder0(BoolQuery.Builder queryBuilder, MatchingVo matchingVo){
        buildQueryBuilder0(queryBuilder,matchingVo);
    }

    public abstract SearchMode getMode();
}
